🎖️GitЯра🎖️
Commit 1aa8c51cd67bec3958a5b63a15641b08f8959b37
Parents : d8b6dd7
Author : copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Signature : Signature validation error
Date : 2026-05-31T14:35:35Z
Committer : GitHub <noreply@github.com>
Date : 2026-05-31T14:35:35Z
feat: save unsent chat message as draft across navigation
Store the message input text in the ViewModel (backed by SavedStateHandle)
so it persists when navigating away and returning to the chat screen.
- Add draftMessage StateFlow to MessageViewModel
- Initialize TextFieldState from ViewModel draft when route message is empty
- Sync text field changes back to ViewModel via snapshotFlow
- Clear draft when message is successfully sent
Changes
2 files changed, 23 insertions(+), 1 deletions(-)
Diff
diff --git a/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt b/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt
index 89ac0ef45b..12adec5341 100644
--- a/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt
+++ b/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt
@@ -51,6 +51,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
@@ -138,12 +139,19 @@ fun MessageScreen(
var showDeleteDialog by rememberSaveable { mutableStateOf(false) }
var sharedContact by rememberSaveable { mutableStateOf<Node?>(null) }
val selectedMessageIds = rememberSaveable { mutableStateOf(emptySet<Long>()) }
- val messageInputState = rememberTextFieldState(message)
+ val draft by viewModel.draftMessage.collectAsStateWithLifecycle()
+ val messageInputState = rememberTextFieldState(message.ifEmpty { draft })
val showQuickChat by viewModel.showQuickChat.collectAsStateWithLifecycle()
val filteredCount by viewModel.filteredCount.collectAsStateWithLifecycle()
val showFiltered by viewModel.showFiltered.collectAsStateWithLifecycle()
val filteringDisabled = contactSettings[contactKey]?.filteringDisabled ?: false
+ // Sync text field changes back to ViewModel draft
+ LaunchedEffect(messageInputState) {
+ snapshotFlow { messageInputState.text.toString() }
+ .collect { text -> viewModel.setDraftMessage(text) }
+ }
+
// Prevent the message TextField from stealing focus when the screen opens
LaunchedEffect(contactKey) { focusManager.clearFocus() }
@@ -233,6 +241,7 @@ fun MessageScreen(
viewModel.sendMessage(event.text, contactKey, event.replyingToPacketId)
if (event.replyingToPacketId != null) replyingToPacketId = null
messageInputState.clearText()
+ viewModel.clearDraftMessage()
}
is MessageScreenEvent.SendReaction ->
diff --git a/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt b/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt
index ca29b38421..aeaa62a3e4 100644
--- a/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt
+++ b/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt
@@ -70,6 +70,19 @@ class MessageViewModel(
private val _title = MutableStateFlow("")
val title: StateFlow<String> = _title.asStateFlow()
+ private val _draftMessage = MutableStateFlow(savedStateHandle.get<String>("draftMessage") ?: "")
+ val draftMessage: StateFlow<String> = _draftMessage.asStateFlow()
+
+ fun setDraftMessage(text: String) {
+ _draftMessage.value = text
+ savedStateHandle["draftMessage"] = text
+ }
+
+ fun clearDraftMessage() {
+ _draftMessage.value = ""
+ savedStateHandle["draftMessage"] = ""
+ }
+
val ourNodeInfo = nodeRepository.ourNodeInfo
val connectionState = serviceRepository.connectionState
Served by rngit 1.5.2 - Generated in 0.13s